classdef exploration
    properties
		constants=[];
    end
    methods
        function self = exploration(constants)
            self.constants = constants;
        end
        
        function prob = egreedy(self,val,vis) % Epsilon greedy policy
            prob = self.constants.epsilon/(sum(~isnan(val))-1);
            prob(val==max(val)) = 1-self.constants.epsilon;
            prob(isnan(val)) = nan;
        end
        
        function prob = softmax(self,val,vis) % Softmax policy
            prob = exp(self.constants.invTemp*val)/nansum(exp(self.constants.invTemp*val));
        end
        
        function prob = lineMatching(self,val,vis) % Linear Matching policy
            prob = val/nansum(val);
        end
        
        function prob = ucb1(self,val,vis) % UCB1 policy with bias constant cp
            prob = zeros(size(val));
            val = val./vis + self.learningConstants.cp *...
                sqrt(repmat(2*log(sum(vis)),size(vis))./vis);
            mval = max(val);
            prob(val==mval) = 1/(sum(val==mval));
        end
        
        function prob = execute(self,policyList,params,val,vis)
            prob = [];
            for i=1:length(policyList)
                if i==1
                    prob = eval(['self.',policyList{i},'(val,vis)'])*params(i);
                else
                    prob = prob + eval(['self.',policyList{i},'(val,vis)'])*params(i);
                end
            end
        end
        
        function funchandle = returnLambda(self,varargin)
            % varargin must be a method name followed by its value. The
            % values should be positive
            if isempty(varargin)
                error('Unspecified method for the returnLambda function');
            elseif mod(length(varargin),2)==1
                error('Uneven number of inputs for returnLambda.\nThe inputs must be [methodName,methodVal]');
            else
                methodList = methods(self);
                methodList(strcmp(methodList,'exploration')==1 | strcmp(methodList,'execute')==1)=[];
                methodList = methodList(:);
                policyList = cell(length(varargin)/2,1);
                params = zeros(size(policyList));
                j=0;
                for i=1:length(varargin)/2
                    ind = find(strcmpi(varargin{2*i-1},methodList)==1,1);
                    if ~isempty(ind) && isnumeric(varargin{2*i})
                        j = j+1;
                        params(j) = varargin{2*i};
                        policyList{j} = methodList{ind};
                    elseif ~isnumeric(varargin{2*i})
                        error('Non-numeric methodVal');
                    end
                end
            end
            params = params(:)/nansum(params);
            inds = params==0 | isnan(params);
            if sum(inds)>0
                policyList = policyList(~inds);
                params = params(~inds);
            end
            funchandle = @(val,vis) self.execute(policyList,params,val,vis);
        end
    end
end